#include "apdu_dev_manager.h"

#include "../../mk_lib/mk_utility.h"
#include "../apdu.algo/sha1.h"
#include "../apdu.algo/mac.h"
#include "../apdu.algo/sm4.h"
#include <string.h>
#include <memory.h>
#include <stdlib.h>

apdu_dev_manager::apdu_dev_manager(void)
{
}

apdu_dev_manager::~apdu_dev_manager(void)
{
}


apdu * apdu_dev_manager::create_apdu_set_label( char * label )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x02, 0x00,0x00, "SetLabel");

	newApdu->set_lc_data((unsigned char*)label, strlen(label));

	return newApdu;
}

apdu * apdu_dev_manager::create_apdu_set_serial_num( char * serialNumber )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x02, 0x01,0x00, "SetSerialNumber");

	newApdu->set_lc_data((unsigned char*)serialNumber, strlen(serialNumber));

	return newApdu;
}


apdu * apdu_dev_manager::create_apdu_get_dev_info()
{
	apdu * newApdu = new apdu(SC_APDU_CASE_2_EXT, 0x80, 0x04, 0x00,0x00, "GetDevInfo");

	return newApdu;
}

apdu * apdu_dev_manager::create_apdu_get_short_dev_info()
{
	apdu * newApdu = new apdu(SC_APDU_CASE_2_EXT, 0x80, 0x04, 0x01,0x00, "GetShortDevInfo");

	return newApdu;
}

apdu * apdu_dev_manager::create_apdu_dev_auth(unsigned char algo, unsigned char * auth_data, int data_len)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x10, 0x00,algo, "DevAuth");
	newApdu->set_lc_data(auth_data, data_len);

	return newApdu;
}


apdu * apdu_dev_manager::create_apdu_change_dev_auth_key( unsigned char algo, unsigned char * secure_new_auth_key, int data_len )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x84, 0x12, 0x00,algo, "ChangeDevAuthKey");

	newApdu->set_lc_data(secure_new_auth_key, data_len);

	return newApdu;
}

apdu * apdu_dev_manager::create_apdu_change_dev_auth_key_ex(unsigned char * new_auth_key, int data_len )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0xC0, 0x36, 0x00,0X00, "ChangeDevAuthKeyEx");

	newApdu->set_lc_data(new_auth_key, data_len);

	return newApdu;
}


apdu * apdu_dev_manager::create_apdu_get_pin_info(unsigned char pin_type, int app_id)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_4_EXT, 0x80, 0x14, 0x00, pin_type, "GetPinInfo");
	
	unsigned char id[2] = {0};
	
	mk_utility::fill_buff_with_word_value_be(app_id, id);
	
	newApdu->set_lc_data(id, 2);

	return newApdu;
	
}

apdu * apdu_dev_manager::create_apdu_change_pin(unsigned char * random, unsigned char pin_type,
											int app_id, char * old_pin, char * new_pin)
{
	unsigned char input[64] = {0};
	strncpy((char*)input, old_pin, 64);

	unsigned char key[20] = {0};
	sha1(input, 16, key);

	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x84, 0x16, 0x00, pin_type, "ChangePin");

	int out_len = 0;
	unsigned char * secure_new_pin = sm_encrypt(key, (unsigned char*)new_pin, 
							strlen(new_pin), &out_len);
	
	unsigned char mac[4] = {0};
	mac_sm4mac_gen(key,random, (unsigned char *)new_pin, strlen(new_pin), mac);

	int pos = 0;
	unsigned char data[128] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	memcpy(data + pos, secure_new_pin, out_len);
	pos += out_len;

	memcpy(data + pos, mac, 4);
	pos += 4;

	free(secure_new_pin);

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_dev_manager::create_apdu_verify_pin(unsigned char * random, unsigned char pin_type,
											int app_id, char * pin)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x18, 0x00, pin_type, "VerifyPin");
	
	unsigned char pin_data[64] = {0};
	strncpy((char*)pin_data, pin, 32);

	int len = strlen((char*)pin_data);
	if(len < 16) len = 16;

	unsigned char key[20] = {0};
	sha1(pin_data, len, key);

	int out_len = 16;
 	unsigned char * sm = sm_encrypt(key, random, 8, &out_len);

	int pos = 0;
	unsigned char data[128] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	memcpy(data + pos, sm, out_len);
	pos += out_len;

	newApdu->set_lc_data(data, pos);

	free(sm);
	return newApdu;
	
}

apdu * apdu_dev_manager::create_apdu_change_pin_ms( unsigned char pin_type, int app_id, unsigned char * acknowlege, int ack_len, unsigned char * new_pin, int nNew_pinLen)
{
	int newPinLen = nNew_pinLen;
	if(newPinLen > 16) 
		newPinLen = 16;

	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x84, 0x16, 0x01, pin_type, "ChangePin_ms");

	int pos = 0;
	unsigned char data[128] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	memcpy(data + pos, acknowlege, ack_len);
	pos += ack_len;

	memcpy(data + pos, new_pin, newPinLen);
	pos += newPinLen;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

//microsoft mode - ack/resp
apdu * apdu_dev_manager::create_apdu_verify_pin_ms(unsigned char pin_type,
												int app_id, unsigned char * acknowlege, int ack_len)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x18, 0x01, pin_type, "VerifyPin_MS");

	int pos = 0;
	unsigned char data[256] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	memcpy(data + pos, acknowlege, ack_len);
	pos += ack_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
}


apdu * apdu_dev_manager::create_apdu_unblock_pin(unsigned char * random, int app_id, char * admin_pin, char * new_upin)
{
	unsigned char pin_data[64] = {0};
	strncpy((char*)pin_data, admin_pin, 64);

	unsigned char user_pin_data[64] = {0};
	strncpy((char*)user_pin_data, new_upin, 64);

	unsigned char key[20] = {0};
	sha1(pin_data, 16, key);

	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x84, 0x1A, 0x00, 0x00, "UnblockPin");

	int out_len = 0;
	unsigned char * secure_pin = sm_encrypt(key, user_pin_data, strlen((char*)user_pin_data), &out_len);

	unsigned char mac[4] = {0};
	mac_sm4mac_gen(key,random, (unsigned char *)new_upin, strlen(new_upin), mac);

	int pos = 0;
	unsigned char data[128] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	
	pos +=2 ;

	memcpy(data + pos, secure_pin, out_len);
	pos += out_len;

	memcpy(data + pos, mac, 4);
	pos += 4;

	free(secure_pin);

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_dev_manager::create_apdu_unblock_pin_ms(int app_id, unsigned char * acknowlege, int ack_len, char * new_upin)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x84, 0x1A, 0x00, 0x01, "UnblockPin_MS");

	int newPinLen = strlen(new_upin);
	if(newPinLen > 16) 
		newPinLen = 16;

	int pos = 0;
	unsigned char data[128] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);

	pos +=2 ;

	memcpy(data + pos, acknowlege, ack_len);
	pos += ack_len;

	memcpy(data + pos, new_upin, newPinLen);
	pos += newPinLen;

	newApdu->set_lc_data(data, pos);
	return newApdu;
}

apdu * apdu_dev_manager::create_apdu_reload_pin( unsigned char random[16], int app_id, char * new_upin )
{
	unsigned char user_pin_data[64] = {0};
	strncpy((char*)user_pin_data, new_upin, 64);

	unsigned char key[20] = {0};
	memcpy(key, random, 16);

	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0xC4, 0x34, 0x00, 0x00, "ReloadPin");

	int out_len = 0;
	unsigned char * secure_pin = sm_encrypt(key, user_pin_data, strlen((char*)user_pin_data), &out_len);

	unsigned char mac[4] = {0};
	mac_sm4mac_gen(key,random, (unsigned char *)new_upin, strlen(new_upin), mac);

	int pos = 0;
	unsigned char data[128] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);

	pos +=2 ;

	memcpy(data + pos, secure_pin, out_len);
	pos += out_len;

	memcpy(data + pos, mac, 4);
	pos += 4;

	free(secure_pin);

	newApdu->set_lc_data(data, pos);

	return newApdu;
}

apdu * apdu_dev_manager::create_apdu_remote_unblock_pin(int app_id, unsigned char * cipher, int cipher_len)
{
	//remote from saved random value of the file
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x84, 0x1A, 0x01, 0x00, "RemoteUnblockPin");

	int pos = 0;
	unsigned char data[128] = {0};
	mk_utility::fill_buff_with_word_value_be(app_id, data);
	pos += 2;

	memcpy(data + pos, cipher, cipher_len);
	pos += cipher_len;

	newApdu->set_lc_data(data, pos);

	return newApdu;
	
}

apdu * apdu_dev_manager::create_apdu_clear_secure_state(int app_id)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x1C, 0x00, 0x00, "ClearSecureState");

	unsigned char id[2] = {0};

	mk_utility::fill_buff_with_word_value_be(app_id, id);

	newApdu->set_lc_data(id, 2);

	return newApdu;
}

apdu * apdu_dev_manager::create_apdu_clear_secure_state_ex(int app_id, int mode)
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0x80, 0x1C, mode, 0x00, "ClearSecureState");

	unsigned char id[2] = {0};

	mk_utility::fill_buff_with_word_value_be(app_id, id);

	newApdu->set_lc_data(id, 2);

	return newApdu;
}

apdu * apdu_dev_manager::create_apdu_sys_led_control( int state, int interval )
{
	apdu * newApdu = new apdu(SC_APDU_CASE_3_EXT, 0xC0, 0x40, 0x00,(unsigned char)state, "LedControl");

	unsigned char in[4];
	mk_utility::fill_buff_with_dword_value_be(interval, in);
	newApdu->set_lc_data(in, 4);

	return newApdu;
}

